﻿using System;
using System.Linq;

namespace ForumsBrowser.CodeFormatting
{
    public class CSharpCodeFormatter : ICodeFormatter
    {
        public static readonly string[] keywords = {
                "abstract", "as", "base", "bool", "break",
                "byte", "case", "catch", "char", "checked",
                "class", "const", "continue", "decimal", "default",
                "delegate", "do", "double", "else", "enum", 
                "event", "explicit", "extern", "false", "finally", 
                "fixed", "float", "for", "foreach", 
                "goto", "if", "implicit", "in", "int", 
                "interface", "internal", "is", "lock", "long", 
                "namespace", "new", "null", "object", "operator", 
                "out", "override", "params", "private", "protected",
                "public", "readonly", "ref", "return", "sbyte", 
                "sealed", "short", "sizeof", "stackalloc",
                "static", "string", "struct", "switch", "this", 
                "throw", "true", "try", "typeof", "uint", 
                "ulong", "unchecked", "unsafe", "ushort", "using", 
                "var", "virtual", "void", "volatile", "while", 
                "add", "alias", "get", "global", "partial", 
                "remove", "set", "value", "where", "yield" };

        public static readonly string[] literalstart = { "@\"", "\"" };
        public static readonly string literalend = "\"";
        public static readonly string escapedquote = "\\\"";
        public static readonly char[] endTokens = { '{', '}', '(', ')', '<', '>', ' ' };
                   
        public string Format(string input)
        {
            string[] lines = input.Split('\n');

            for (int i = 0; i < lines.Length; i++)
                lines[i] = FormatLine(lines[i]);

            return string.Join("<br/>", lines);
        }

        private static string FormatLine(string input)
        {
            string output = string.Empty;
            string token = string.Empty;
            bool inSpan = false;
            //bool inString = false;

            for (int i = 0; i < input.Length; i++)
            {
                token += input[i];
                bool endOfLine = i + 1 == input.Length;
                bool endToken = endOfLine || endTokens.Contains(input[i + 1]);
                
                if (endToken)
                {
                    if (IsKeyword(token) && !inSpan)
                    {
                        token = "<span style=\"color:blue\">" + token;
                        inSpan = true;
                    }
                    else if (!IsKeyword(token) && inSpan)
                    {
                        token = "</span>" + token;
                        inSpan = false;
                    }
                    else if (inSpan && IsKeyword(token) && endToken && (!endOfLine) && input[i + 1] != ' ')
                    {
                        token += "</span>";
                        inSpan = false;
                    }

                    output += token;
                    token = string.Empty;
                }

                if (endOfLine && inSpan)
                {
                    output += "</span>";
                    inSpan = false;
                }
            }

            return output;
        }


        private static bool IsKeyword(string token)
        {
            for (int i = 0; i < keywords.Length; i++)
            {
                if (token.EndsWith(keywords[i]))
                    return true;
            }
            return false;
        }
    }
}
